package org.appFactory.facebook;

import java.security.SecureRandom;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URLEncoder;
import org.appFactory.shared.Util;
import org.json.JSONException;
import org.json.JSONObject;
import org.appFactory.shared.Constants;
import org.json.JSONArray;

public class FacebookServiceImpl implements FacebookService {

	private static final String FB_API_KEY = "127368214064019";
	private static final String FB_API_SECRET = "ff2033fa2b09722e7c5af674c92618db";
	private static final String FB_ACCESS_CODE = "FB_ACCESS_CODE";
	private static final String FB_URL = "https://www.facebook.com/";
	private static final String FB_GRAPH_URL = "https://graph.facebook.com/";
	private static final String FB_PLACES_URL = FB_GRAPH_URL + "search?type=place";

	private String accessToken = null;
	
	private SecureRandom rand;

	public String getOAuthDialogURL(String redirectURI) {
		String stateNonce = getNonce();
		final StringBuilder sb = new StringBuilder(FB_URL);
		sb.append("dialog/oauth?client_id=").append(FB_API_KEY);
		sb.append("&redirect_uri=").append(redirectURI);
		sb.append("&scope=user_about_me,user_events,friends_events");
		sb.append("&state=").append(stateNonce);
		return sb.toString();
	}

	public String getAccessTokenURL(String code, String redirectURI) {
		final StringBuilder sb = new StringBuilder(FB_GRAPH_URL);
		sb.append("oauth/access_token?client_id=").append(FB_API_KEY);
		sb.append("&redirect_uri=").append(redirectURI);
		sb.append("&client_secret=").append(FB_API_SECRET);
		sb.append("&code=").append(code);
		return sb.toString();
	}
		
	public String exchangeCodeForToken(String code, String redirectURI) {
		// TODO : Compare the state Nonce with the one used in getOAuthDialogURL for protection
		// against man-in-the-middle attack
		String stateNonce = getNonce();
		final StringBuilder sb = new StringBuilder(FB_GRAPH_URL);
		sb.append("oauth/access_token?client_id=").append(FB_API_KEY);
		sb.append("&redirect_uri=").append(redirectURI);
		sb.append("&client_secret=").append(FB_API_SECRET);
		sb.append("&code=").append(code);
		System.out.println("Authentication to Facebook: " + sb.toString());

		String result = Util.getResponse(sb.toString());
		try {
			String[] eparts = result.split("=");

			if (eparts.length < 2) {
				return Constants.ERROR;
			}
			String [] parts = eparts[1].split("&");

			if (parts.length < 2) {
				return Constants.ERROR;
			}
			accessToken = "access_token=" + URLEncoder.encode(parts[0], "UTF-8");
			return accessToken;
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return Constants.ERROR;
	}
	
	public void setAccessToken(String accessToken) {
		this.accessToken = accessToken;
	}

	
	public JSONObject getUserInfo() throws JSONException {
		JSONObject fbProfile = Util.getJSONResponse(getUserInfoURL());
		System.out.println("Profile " +getUserInfoURL() +", Response: "+ fbProfile.toString());
		JSONObject userProfile = new JSONObject();
		userProfile.put("name", fbProfile.get("name"));
		userProfile.put("picURL", getUserPicURL());
		return userProfile;
	}
	
	public String getAccessTokenKey() {
		return FB_ACCESS_CODE;
	}
	
	// Private functions or utilities
	private String getUserInfoURL() {
		final StringBuilder sb = new StringBuilder(FB_GRAPH_URL);
		sb.append("me?&").append(accessToken);
		return sb.toString();
	}
	
	private String getUserPicURL() {
		final StringBuilder sb = new StringBuilder(FB_GRAPH_URL);
		sb.append("me/picture&").append(accessToken);
		return sb.toString();
	}
	
	private String getNonce(){
		//The following operation is expensive, so just create one instance per session.
		if (rand == null) {
			rand = new SecureRandom();
		}
		return new BigInteger(130, rand).toString(32);
	}

	
	private String getEventsURL(String id){
		StringBuffer sb = new StringBuffer(FB_GRAPH_URL);
		sb.append(id).append("/events?&").append(accessToken);
		return sb.toString();
	}
	
	
	private String getPlacesURL(String latitude, String longitude, Integer distance) {
		if(distance==null)
			distance=1000;
		StringBuffer sb = new StringBuffer(FB_PLACES_URL);
		sb.append("&center=").append(latitude+",").append(longitude);
		sb.append("&distance=").append(distance+"&").append(accessToken);
		System.out.println(sb.toString());
		return sb.toString();
	}

	/*
	 * Given latitude and longitude, will fetch events happening
	 * in that place within 'distance' range including friends information
	 * who are attending that event.
	 */
	@Override
	public JSONObject getEvents(String latitude, String longitude,Integer distance) {
		//Fetch Places
		JSONObject placesData = Util.getJSONResponse(getPlacesURL(latitude, longitude,distance));
		//Initialize Event Objects
		JSONObject allEvents = new JSONObject();
		JSONArray allEventsData = new JSONArray();
		
		try {
			String next=null;
			if(!placesData.isNull("data"))			//first level places
				do{
					JSONArray places = placesData.getJSONArray("data");
					for(int i=0;i<places.length();i++){
						JSONObject locationObj = new JSONObject();
						locationObj.put("location", ((JSONObject)places.get(i)).get("location"));
						JSONObject eventsData = ((JSONObject)Util.getJSONResponse(getEventsURL(((JSONObject)places.get(i)).getString("id"))));
						//System.out.println("Events URL: " + getEventsURL(((JSONObject)places.get(i)).getString("id")) + ", Events JsON : " + eventsData.toString());
						if(!eventsData.isNull("data")){  					//second level events
							do{
								JSONArray events = eventsData.getJSONArray("data");
								if(events.length()!=0)
									for(int k=0;k<events.length();k++){						//creating friend keys
										JSONObject temp = (JSONObject)events.get(k);
										temp.put("venue", locationObj);
										allEventsData.put(temp);
									}								
							}while((!eventsData.isNull("paging") && (eventsData=((JSONObject)eventsData.get("paging")))!=null) && (!eventsData.isNull("next") //paginated Results events 
									&& (next=eventsData.getString("next"))!=null) && (eventsData=Util.getJSONResponse(next))!=null && !eventsData.isNull("data"));
						}
					}					
				}while((!placesData.isNull("paging") && (placesData=((JSONObject)placesData.get("paging")))!=null) && (!placesData.isNull("next") 			//paginated Results places
						&& (next=placesData.getString("next"))!=null) && (placesData=Util.getJSONResponse(next))!=null && !placesData.isNull("data"));
			
			allEvents.put("data", allEventsData);	
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//return addFriendsForEvents();
		return allEvents;
	}




}
